package org.jiamandan.com.config;

import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jiamandan.com.cache.CacheNames;
import org.jiamandan.com.constant.BizConstant;
import org.jiamandan.com.constant.DXConstant;
import org.jiamandan.com.dto.DXBasicDto;
import org.jiamandan.com.strategy.DXContextService;
import org.jiamandan.com.strategy.listener.DXF03UpgradeListener;
import org.jiamandan.com.utils.DXObjectUtils;
import org.jiamandan.com.utils.RedisUtil;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Component
public class UdpServerHandler extends SimpleChannelInboundHandler<UdpHexMessage> {
    public static  ChannelGroup clients;
	public static Map<String, DatagramPacket> maps = new ConcurrentHashMap<>();
    public static RedisUtil redisUtil;
    public static DXContextService dxContextService;
    private static Integer udpSequence = 1003;
    DXF03UpgradeListener dpUpgradeListener;

    static {
        clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        redisUtil = SpringUtil.getBean(RedisUtil.class);
        dxContextService = SpringUtil.getBean(DXContextService.class);
    }

    public static Integer getUdpSequence() {
        Object redisUpd = redisUtil.get(CacheNames.UDP_SEQUENCE);
        if(null == redisUpd){
            udpSequence = 0;
        }else{
            udpSequence++;
        }
        redisUtil.set(CacheNames.UDP_SEQUENCE,udpSequence);
        return  udpSequence;
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, UdpHexMessage msg) {
        try {
            InetSocketAddress localSocket = (InetSocketAddress) ctx.channel().localAddress();
            int port = localSocket.getPort();
            Channel client = ctx.channel();
             String dxPayload = msg.getHexData();
             if(BizConstant.HardSoftFactoryPort.JMD_DX == port){
                 //getUdpSequence();
                InetSocketAddress sender = msg.getSender();
               // log.info("接受嘉满电-DX桩报文,端口：18085:【" + msg.getHexData() + "】");
                DXBasicDto dto = DXObjectUtils.parse(dxPayload);
                if(dto.getDeviceNo().indexOf("0F03")>-1){
                    //执行升级任务
                  //  dpUpgradeListener.upgradeDevice(msg.getHexData(),client);
                    upgradeDevice(msg.getHexData(),client,sender);
                    return;
                }
                String deviceNo = dto.getDeviceNo();
                /*if (getSocketAddress(deviceNo, msg,ctx.channel().id().asShortText()) == null) {
                    updateCacheByDeviceNo(deviceNo, client,msg);
                }*/
                 // 更新设备状态
                updateDeviceStatus(deviceNo, sender,client);
                dxContextService.execute(dto, client);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private void upgradeDevice(String hexData, Channel client,InetSocketAddress address) {
        String offset = StringUtils.substring(hexData, 40, 48);
        String result =  StringUtils.substring(hexData, 60, 62);
       // if(result.equals("00")){
            //执行成功
            int i = Integer.parseInt(offset, 16);
            //log.info("嘉满电-DX【升级命令】执行升级任务成功，当前偏移量={},十进制数据={},现在需要的偏移量={}",offset,i,i+512);
            String response = (String)redisUtil.hget("DX_UPGRADE_VALUE", String.valueOf((i + 512)));
            if(response == null){
                log.info("嘉满电-DX【升级命令】没有数据了，结束升级任务");
                return;
            }
            DXBasicDto dto = new DXBasicDto();
            dto.setReserved("0000000000");
            dto.setProtocol("01");
            dto.setDeviceNo("");
            String payload = DXObjectUtils.replyMessage(response, dto, "00", DXConstant.Cmd.CMD_F03);
            //log.info("嘉满电-DX0F03发送命令(CMD={}),发送内容={}",DXConstant.Cmd.CMD_F03,payload);
            DatagramPacket datagramPacket = new DatagramPacket(
                    hexToByteBuf(payload),
                    address
            );
            senderInternal(datagramPacket,client);
    }



    private void sendMsg(String deviceNo, String version,Integer curPoint){
        DXBasicDto dto = new DXBasicDto();
         dto.setReserved("0000000000");
        dto.setProtocol("01");
        dto.setDeviceNo(deviceNo);
        String response = (String)redisUtil.hget("DX_UPGRADE_VALUE", curPoint.toString());
        UdpServerHandler.sendMessage(response,dto,"", DXConstant.Cmd.CMD_F03);
    }

    //更新设备状态
    private void updateDeviceStatus(String deviceNo, InetSocketAddress address, Channel client) {
       Map<String, Object> tempMap = new HashMap<>();
        tempMap.put("ip", address.getHostString());
        tempMap.put("port", address.getPort());
        tempMap.put("lastActive", System.currentTimeMillis());
        Map<String, Object> status = Collections.unmodifiableMap(tempMap);
        redisUtil.hset(CacheNames.DEVICE_STATUS_CACHE_KEY, deviceNo,status);

        //添加udp_channel
        ChannelId channelId = getChannelId(deviceNo, client.id().asShortText());
        if( channelId == null){
            Map<String, ChannelId> channelResult = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
            if (channelResult == null) {
                channelResult = new HashMap<>();
            }
            clients.add(client);
            channelResult.put(deviceNo, client.id());
            redisUtil.set(CacheNames.UDP_CHANNEL, channelResult);
            log.info(deviceNo + "添加到client-UDP中,channelId="+clients.find(client.id()));
        }
    }


    private ChannelId getChannelId(String deviceNo, String oldChannelId) {
        Map<String, ChannelId> result = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
        if (result == null || result.get(deviceNo) == null) {
            return null;
        }
        ChannelId channelId = result.get(deviceNo);
        if (!oldChannelId.equals(channelId.asShortText())) {
            log.info("UDP数据新进channel->移除旧连接:" + channelId);
            clients.remove(channelId);
            this.removeCacheByChannelId(channelId);
            return null;
        }
        if (clients.find(channelId) != null) {
            return channelId;
        } else {
            log.info("UDP数据->移除旧连接:" + channelId);
            removeCacheByChannelId(channelId);
            return null;
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("Channel error: {}", cause.getMessage());
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }



    /**
     * 获取socket_address
     * @param deviceNo
     * @param msg
     * @return
     */
    private InetSocketAddress getSocketAddress(String deviceNo, UdpHexMessage msg,String oldChannelId) {
        InetSocketAddress sender = msg.getSender();
        Map<String, ChannelId> result = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
        Map<String, InetSocketAddress> resultUpd = (Map<String, InetSocketAddress>) redisUtil.get(CacheNames.UPD_ADDRESS);
        if (resultUpd == null || resultUpd.get(deviceNo) == null) {
            return null;
        }
        InetSocketAddress oldSocketAddress =  resultUpd.get(deviceNo);
        if (!sender.getHostString().equals(oldSocketAddress.getHostString())) {
            log.info("移除旧连接-UDP,添加新链接={}", oldSocketAddress.getHostString());
            this.removeCacheByUpMessage(msg);
            return null;
        }
        if(result == null || result.get(deviceNo) == null){
            return null;
        }
        ChannelId channelId = result.get(deviceNo);
        log.info("当前缓存channelId：【" + channelId + "】接收消息的channelId:【" + oldChannelId + "】"+"deviceNo:【"+deviceNo+"】");
        if (!oldChannelId.equals(channelId.asShortText())) {
            log.info("移除旧连接:" + channelId);
            clients.remove(channelId);
            this.removeCacheByChannelId(channelId);
            return null;
        }
        return sender;
    }

    public static String getDatagramPacket(String deviceNo){
        return JSONObject.toJSONString(redisUtil.get(deviceNo));
    }

    /**
     * @param deviceNo
     * @param client
     */
    private void updateCacheByDeviceNo(String deviceNo, Channel client,UdpHexMessage msg) {
        ChannelId channelId = client.id();
        Map<String, InetSocketAddress> result = (Map<String, InetSocketAddress>) redisUtil.get(CacheNames.UPD_ADDRESS);
        if (result == null) {
            result = new HashMap<>();
        }
        result.put(deviceNo, msg.getSender());
        //clients.add(client);
        redisUtil.set(deviceNo+"-ip",msg.getSender().getHostString());
        redisUtil.set(deviceNo+"-port",msg.getSender().getPort());
        log.info("======" + deviceNo + "====" + channelId + "===============客户端数量-UDP："+clients.size()+"===redis存储数量:"+result.size());
        redisUtil.set(CacheNames.UPD_ADDRESS, result);


        //channel 添加
        Map<String, ChannelId> channelResult = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
        clients.add(client);
        if (channelResult == null) {
            channelResult = new HashMap<>();
        }
        channelResult.put(deviceNo, channelId);
        redisUtil.set(CacheNames.UDP_CHANNEL, channelResult);
        log.info(deviceNo + "添加到client-UDP中,channelId="+clients.find(channelId));
    }

    /**
     * 删除Message
     * @param msg
     */
    private void removeCacheByUpMessage(UdpHexMessage msg) {
        Map<String, InetSocketAddress> result = (Map<String, InetSocketAddress>) redisUtil.get(CacheNames.UPD_ADDRESS);
        if (result == null) {
            return;
        }
        for (Map.Entry<String, InetSocketAddress> entry : result.entrySet()) {
            if (entry.getValue().getHostString().equals(msg.getSender().getHostString())) {
                result.remove(entry.getKey());
                break;
            }
        }
        redisUtil.set(CacheNames.UPD_ADDRESS, result);
    }

   private void removeCacheByChannelId(ChannelId channelId) {
        Map<String, ChannelId> result = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
        if (result == null) {
            return;
        }
        for (Map.Entry<String, ChannelId> entry : result.entrySet()) {
            if (entry.getValue().asShortText().equals(channelId.asShortText())) {
                result.remove(entry.getKey());
                break;
            }
        }
        redisUtil.set(CacheNames.UDP_CHANNEL, result);
    }


    public static void  sendMessage(String response, DXBasicDto dto,String gunNo,  String cmd) {
        Map<String, ChannelId> channels = (Map<String, ChannelId>) redisUtil.get(CacheNames.UDP_CHANNEL);
        String deviceNo = dto.getDeviceNo();
        if (channels == null || channels.get(deviceNo) == null) {
            log.error("未找到相关发送通道ID");
        }
        ChannelId channelId = channels.get(deviceNo);
        Channel channel = UdpServerHandler.clients.find(channelId);
        if (channel == null) {
            log.error("未找到相关发送通道ID");
        }
        Object statusJson = redisUtil.hget(CacheNames.DEVICE_STATUS_CACHE_KEY, dto.getDeviceNo());
        if (statusJson == null) {
            log.error("设备 {} 未注册或已下线", deviceNo);
            return;
        }
        String payload = "";
        if(cmd.equals("0F03")){
            dto.setDeviceNo("");
            payload = DXObjectUtils.replyMessage(response, dto, gunNo, cmd);
            //log.info("嘉满电-DX0F03发送命令(CMD={}),回复内容={}",cmd,payload);
            DatagramPacket datagramPacket = datagramPacket(payload.toString(),statusJson);
            senderInternal(datagramPacket,channel);
        }else{
            payload = DXObjectUtils.replyMessage(response, dto, gunNo, cmd);
            log.info("嘉满电-DX发送命令(CMD={}),回复内容={}",cmd,payload);
            DatagramPacket datagramPacket = datagramPacket(payload.toString(),statusJson);
            senderInternal(datagramPacket,channel);
        }
    }


     /**
     * 发送数据包并监听结果
     * @param packet
     */
    public static void senderInternal(DatagramPacket packet, Channel channel) {
        try {
            ChannelFuture sync = channel.writeAndFlush(packet).sync();// 发送数据
            if(sync.isSuccess()){
                log.info("成功！={}",sync);
            }else{
                log.info("失败！={}",sync);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 组装数据包
     * @param response
     * @param statusJson
     * @return
     */
    private static DatagramPacket datagramPacket(String response, Object statusJson){
         try {
            // 将消息包装成DatagramPacket
            Map<Object, Object> status = JSON.parseObject(JSONObject.toJSONString(statusJson), Map.class);
            InetSocketAddress address = new InetSocketAddress(
                    (String) status.get("ip"),
                    ((Number) status.get("port")).intValue()
            );
            return new DatagramPacket(
                    hexToByteBuf(response),
                    address
            );
        } catch (Exception e) {
            log.error("构造报文失败: {}", e.getMessage());
        }
         return null;
    }
    public static ByteBuf hexToByteBuf(String hexString) {
        // 清理输入：移除所有非 HEX 字符（如空格、0x 前缀）
        String cleanedHex = hexString.replaceAll("[^0-9A-Fa-f]", "").toUpperCase();

        //是否是偶数位
        if (cleanedHex.length() % 2 != 0) {
            throw new IllegalArgumentException("Invalid HEX string length (must be even)");
        }

        // 3. 转换为字节数组
        byte[] bytes = new byte[cleanedHex.length() / 2];
        for (int i = 0; i < bytes.length; i++) {
            int index = i * 2;
            String hexPair = cleanedHex.substring(index, index + 2);
            bytes[i] = (byte) Integer.parseInt(hexPair, 16);
        }

        // 4. 包装为 Netty ByteBuf（无需手动释放）
        return Unpooled.wrappedBuffer(bytes);
    }

}
